Reverse Proxy Patterns
Common reverse proxy design patterns for self-hosted services and internal platforms
Introduction
A reverse proxy accepts client requests and forwards them to upstream services. It commonly handles TLS termination, host-based routing, request header forwarding, and policy enforcement in front of self-hosted applications.
Purpose
Reverse proxies are used to:
- Publish multiple services behind one or a few public entry points
- Centralize TLS certificates
- Apply authentication, authorization, or rate-limiting controls
- Simplify backend service placement and migration
Architecture Overview
Typical request flow:
Client -> Reverse proxy -> Upstream applicationCommon proxy responsibilities:
- TLS termination and certificate management
- Routing by hostname, path, or protocol
- Forwarding of
Host, client IP, and other headers - Optional load balancing across multiple backends
Common Patterns
Edge proxy for many internal services
One proxy handles traffic for multiple hostnames:
grafana.example.comgitea.example.comvault.example.com
This is a good default for small homelabs and internal platforms.
Internal proxy behind a VPN
Administrative services are reachable only through a private network such as Tailscale, WireGuard, or a dedicated management VLAN. This reduces public attack surface.
Path-based routing
Useful when hostnames are limited, but more fragile than host-based routing because some applications assume they live at /.
Dynamic discovery proxy
Tools such as Traefik can watch container metadata and update routes automatically. This reduces manual config for dynamic container environments, but it also makes label hygiene and network policy more important.
Configuration Example
NGINX example:
server {
listen 443 ssl http2;
server_name app.example.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8080;
}
}Caddy example:
app.example.com {
reverse_proxy 127.0.0.1:8080
}Troubleshooting Tips
Application redirects to the wrong URL
- Check forwarded headers such as
HostandX-Forwarded-Proto - Verify the application's configured external base URL
- Confirm TLS termination behavior matches application expectations
WebSocket or streaming traffic fails
- Check proxy support for upgraded connections
- Review buffering behavior if the application expects streaming responses
Backend works locally but not through the proxy
- Verify the proxy can reach the upstream host and port
- Check the proxy network namespace if running in a container
- Confirm firewall rules permit the proxy-to-upstream path
Best Practices
- Prefer host-based routing over deep path rewriting
- Publish only the services that need an edge entry point
- Keep proxy configuration under version control
- Use separate internal and public entry points when trust boundaries differ
- Standardize upstream headers and base URL settings across applications